using System;
using System.Linq;
using Generator.Model;

namespace Generator.Renderer.Public;

internal static class OpaqueTypedRecord
{
    public static string Render(GirModel.Record record)
    {
        if (record.FreeFunction is null)
            Log.Information($"Record {Model.Type.GetPublicNameFullyQuallified(record)}: Has no free-func annotation. Define it upstream to improve performance for this type.");

        if (record.CopyFunction is null)
            Log.Information($"Record {Model.Type.GetPublicNameFullyQuallified(record)}: Has no copy-func annotation. Define it upstream to improve performance for this type.");

        var name = Model.OpaqueTypedRecord.GetPublicClassName(record);
        var internalHandleName = Model.OpaqueTypedRecord.GetFullyQuallifiedOwnedHandle(record);

        return $@"
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

#nullable enable

namespace {Namespace.GetPublicName(record.Namespace)};

// AUTOGENERATED FILE - DO NOT MODIFY

{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)}
public sealed partial class {name} : GLib.BoxedRecord, GObject.GTypeProvider, GObject.InstanceFactory, IEquatable<{name}>, IDisposable
{{
    public {internalHandleName} Handle {{ get; }}

    public {name}({internalHandleName} handle)
    {{
        Handle = handle;
        Handle.SetMemoryPressure();
    }}

    static object GObject.InstanceFactory.Create(IntPtr handle, bool ownsHandle)
    {{
        var safeHandle = ownsHandle
            ? new {Record.GetFullyQualifiedInternalOwnedHandle(record)}(handle)
            : {Record.GetFullyQualifiedInternalOwnedHandle(record)}.FromUnowned(handle);

        return new {name}(safeHandle);
    }}

    {record.Constructors
        .Select(ConstructorRenderer.Render)
        .Join(Environment.NewLine)}

    public static GObject.Type GetGType()
    {{
        return {RenderGetGType(record.TypeFunction!)};
    }}

    {record.Functions
        .Select(FunctionRenderer.Render)
        .Join(Environment.NewLine)}

    {record.Methods
        .Where(Method.IsEnabled)
        .Select(MethodRenderer.Render)
        .Join(Environment.NewLine)}

    System.IntPtr GLib.BoxedRecord.GetHandle()
    {{
        return Handle.DangerousGetHandle();
    }}

    public bool Equals({name}? other)
    {{
        if (ReferenceEquals(null, other))
            return false;

        if (ReferenceEquals(this, other))
            return true;

        return Handle.Equals(other.Handle);
    }}

    public override bool Equals(object? obj)
    {{
        return ReferenceEquals(this, obj) || obj is {name} other && Equals(other);
    }}

    public override int GetHashCode()
    {{
        return Handle.GetHashCode();
    }}

    partial void OnDispose();

    public void Dispose()
    {{
        OnDispose();
        Handle.Dispose();
    }}
}}";
    }

    private static string RenderGetGType(GirModel.Function function)
    {
        return $"{Namespace.GetInternalName(function.Namespace)}.{function.Parent!.Name}.{Function.GetName(function)}()";
    }
}
